#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
#include <stdlib.h>
#include "util.h"
#include "animations.h"
#include "font.h"
#include "displaystring-iconv.h"

uint8_t buf1[] = {
	0b00111100,
	0b11000011,
	0b10100101,
	0b10000001,
	0b10100101,
	0b10011001,
	0b01000010,
	0b00111100
};

uint8_t buf2[] = {
	0b00111100,
	0b11000011,
	0b10100001,
	0b10000001,
	0b10100101,
	0b10011001,
	0b01000010,
	0b00111100
};

uint8_t *buf[] = {buf1, buf2};
uint8_t frameBufferIdx = 0;

void animationBufferSwap( void )
{
	SWAP_BUFFER_INDEX();
}

void setAnimationFunction( ANIMATION animFunc, uint8_t resetFrame, uint8_t activateAnimation, uint8_t loopAnimation )
{
	animation.currentAnimation = animFunc;

	CLEAR_BUFFER( FRAME() );
	CLEAR_BUFFER( BUFFER() );

	if( resetFrame )
	{
		animation.currentFrame = 0;
	}
	
	animation.ticksForCurrentFrame = 0;

	animation.active = activateAnimation;
	animation.loop = loopAnimation;
}

void animationTick( void )
{
	int8_t ticks;

	//cannot get here with animation.active == FALSE because in main we get a jump before animationTick
	if( !animation.active )
	//if( !animation.tick )
	{
		return;
	}

	if( animation.ticksForCurrentFrame == 0 )
	{
		//creates the next animation frame in the background buffer
		ticks = (*animation.currentAnimation)();
		if( ticks < 0 )
		{
			CLEAR_BUFFER( FRAME() );
			CLEAR_BUFFER( BUFFER() );
			if( animation.loop )
			{
				animation.currentFrame = 0;
			}
			else
			{
				animation.active = FALSE;			
			}
		}
		else
		{
			animation.nextFrameReady = TRUE;
			animation.ticksForCurrentFrame = ticks;
			animation.currentFrame++;
		}
	}
	else
	{
		animation.ticksForCurrentFrame--;
	}

}


/**
 * Animations and the list of active one defined here
 * 
 */
ANIMATION animationList[] = {
		anim_scrollText,
		anim_blinkrect2, 
		anim_playpong, 
		anim_ski, 
		anim_movingDot,
		anim_fillDot,
		anim_linerun,
		anim_spiral
};

//this should display battery level
extern uint8_t batteryLevel;
int8_t anim_batteryLevel( void )
{
	if( animation.currentFrame >= 8 )
	{
		return -1;
	}

	CLEAR_BACK_BUFFER();

	// uint8_t mask = ( _BV( batteryLevel ) - 1 ) | _BV( batteryLevel );

	for( uint8_t i = 0; i < 8; i++ )
	{
		if( i<= batteryLevel )
		{
			BUFFER()[ i ] = 0b00011000;
		}
		else
		{
			BUFFER()[ i ] = 0;
		}
		//BUFFER()[ i ] = mask;
	}

	return 100;	
}


int8_t anim_movingDot( void )
{
	if( animation.currentFrame >= NUMBER_OF_LEDS )
	{
		return -1;
	}

	CLEAR_BACK_BUFFER();
	BUFFER()[ animation.currentFrame / 8 ] |= _BV( animation.currentFrame % 8 );
	BUFFER()[ animation.currentFrame % 8 ] |= _BV( animation.currentFrame / 8 );

	return 18;		
}

int8_t anim_fillDot( void )
{
	if( animation.currentFrame >= NUMBER_OF_LEDS )
	{
		return -1;
	}

	COPY_FRAME_TO_BUFFER();
	BUFFER()[ animation.currentFrame / 8 ] |= _BV( animation.currentFrame % 8 );
	BUFFER()[ animation.currentFrame % 8 ] |= _BV( animation.currentFrame / 8 );

	return 10;	
}

//should find a spiral formula
int8_t anim_spiral( void )
{
	static int8_t movx, movy, movswp;
	static uint8_t posx, posy;
	static const uint8_t posval[] = {8,7,7,6,6,5,5,4,4,3,3,2,2,1,1};
	static uint8_t posvalidx;
	static uint8_t posvalcnt;

	if( animation.currentFrame >= NUMBER_OF_LEDS )
	{
		return -1;
	}

	if( animation.currentFrame == 0 )
	{
		posx = 0;
		posy = 0;
		movx = 1;
		movy = 0;
		posvalidx = 0;
		posvalcnt = posval[ posvalidx ];
	}

	COPY_FRAME_TO_BUFFER();	
	BUFFER()[ posy ] |= _BV( 7-posx );
	
	posvalcnt--;
	if( posvalcnt == 0 )
	{
		movswp = movx;
		movx = -1*movy;
		movy = movswp;
		posvalidx++;
		posvalcnt = posval[ posvalidx ];			
	}

	posx += movx;
	posy += movy;	

	return (74-animation.currentFrame);	
}

int8_t anim_showFont( void )
{
	static uint16_t charIdx;
	uint8_t idx;
	
	if( animation.currentFrame > 221 )
	{
		charIdx = 0;
		return -1;
	}

	CLEAR_BACK_BUFFER();

	for( idx = 0; idx < 8; idx++ )
	{
		BUFFER()[ idx ] = pgm_read_byte( font + charIdx + idx );
	}
	charIdx += 8;
	
	return 127;
}

int8_t anim_scrollText( void )
{
	eetext mytext;
	uint8_t cframe,lbyte,rbyte,cidx,nidx,rest;
	uint8_t idx;
	
	//read text from eeprom
	eeprom_read_block( &mytext, &cycledText, sizeof( eetext ) );

	cframe = animation.currentFrame;
	cidx = cframe >> 3;
	rest = cframe & 0b111;
	
	if( ( rest == 0 ) && ( cidx == ( mytext.length - 1 ) ) )
	{
		return -1;
	}

	CLEAR_BACK_BUFFER();

	if( rest )
	{
		nidx = ( cidx + 1 );
		for( idx = 0; idx < 7; idx++ )
		{
			lbyte = FONT_CHAR_LINE_BYTE( mytext.text[cidx] , idx );
			lbyte <<= rest;

			rbyte = FONT_CHAR_LINE_BYTE( mytext.text[nidx] , idx );
			rbyte >>= 8-rest;

			BUFFER()[ idx+1 ] = lbyte | rbyte;
		}		
	}
	else
	{
		for( idx = 0; idx < 7; idx++ )
		{
			BUFFER()[ idx+1 ] = FONT_CHAR_LINE_BYTE( mytext.text[cidx],idx );
		}
	}
		
	return 25;
}

int8_t anim_ski( void )
{
	static uint8_t nextPos;
	static uint8_t delayInc = 0;
	static int8_t delayDir = 1;
	uint8_t dir;


	if( animation.currentFrame == 0 )
	{
		srand( nextPos );
		nextPos = (uint8_t)(rand() & 0b111) % 6;
		nextPos++;
	}
	else
	{
		memcpy( BUFFER()+1, FRAME(), 7 );
		dir = (uint8_t)(rand() & 0b1);
		if( dir && nextPos < 6 )
		{
			nextPos++;
		}
		if( !dir && nextPos > 1 )
		{
			nextPos--;
		}
		delayInc += delayDir;
		if( delayInc == 15 )
		{
			delayDir = -1;
		}
		else if( delayInc == 0 )
		{
			delayDir = 1;
		}
	}
	BUFFER()[ 0 ] = _BV( nextPos -1 ) | _BV( nextPos ) | _BV( nextPos + 1 );		

	return 25 + delayInc;
}

int8_t anim_linerun( void )
{
	if( animation.currentFrame >= 8 )
	{
		return -1;
	}

	CLEAR_BACK_BUFFER();

	BUFFER()[ animation.currentFrame ] = 0xFF;
	if( animation.currentFrame )
	{
		BUFFER()[ animation.currentFrame-1 ] = 0xFF;
	}

	return 30;		
}

int8_t anim_blinkrect( void )
{
	if( animation.currentFrame >= 2 )
	{
		return -1;
	}

	if( animation.currentFrame & 1 )
	{
		FILL_BUFFER( BUFFER(), 0b11000011 );
	}
	else
	{
		FILL_BUFFER( BUFFER(), 0b111100 );
	}

	return 60;		
}

int8_t anim_blinkrect2( void )
{
	if( animation.currentFrame >= 4 )
	{
		return -1;
	}

	CLEAR_BACK_BUFFER();

	if( animation.currentFrame == 0 )
	{
		memset( BUFFER(), 0b11110000, 4 );
	}
	if( animation.currentFrame == 1 )
	{
		memset( BUFFER(), 0b1111, 4 );
	}
	if( animation.currentFrame == 2 )
	{
		memset( BUFFER()+4, 0b1111, 4 );
	}
	if( animation.currentFrame == 3 )
	{
		memset( BUFFER()+4, 0b11110000, 4 );
	}

	return 60;		
}

typedef struct _mPoint
{
	union
	{
		struct
		{
			uint8_t xPos : 4;
			uint8_t yPos : 4;			
		};
		uint8_t posByte;
	} position;
} mPoint;

int8_t anim_playpong( void )
{
	static mPoint ballPos = 
	{
		.position.xPos = 2,
		.position.yPos = 5 
	};
	static int8_t hDir = 1;
	static int8_t vDir = 1;

	CLEAR_BACK_BUFFER();

	BUFFER()[ ballPos.position.yPos ] |= _BV( ballPos.position.xPos );

	if( ballPos.position.xPos == 0 )
	{
		hDir = 1;
		vDir = (rand()&1) ? 1 : -1;
		if( rand()&1 )
		{
			vDir = 0;
		}
	}
	if( ballPos.position.xPos == 7 )
	{
		hDir = -1;
		vDir = (rand()&1) ? 1 : -1;
		if( rand()&1 )
		{
			vDir = 0;
		}
	}
	ballPos.position.xPos += hDir;

	if( ballPos.position.yPos == 0 )
	{
		vDir = (rand()&1) ? 1 : 0;
	}
	if( ballPos.position.yPos == 7 )
	{
		vDir = (rand()&1) ? 0 : -1;
	}
	ballPos.position.yPos += vDir;


	return 25;
}
